/**
* 版权所有：福建邮科电信业务部厦门研发中心 
*====================================================
* 文件名称: AbstractDao.java
* 修订记录：
* No    日期				作者(操作:具体内容)
* 1.    2013-3-13			詹保山(创建:创建文件)
*====================================================
* 类描述：(说明未实现或其它不应生成javadoc的内容)
* 
*/
package com.devframe.sysbase.dao;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

public class AbstractDao {
	  /**
     * MACRO_END
     */
    protected static final String MACRO_END = "}";

    /**
     * MACRO_BEGIN
     */
    protected static final String MACRO_BEGIN = "${";

    /**
     * PARAM_SQL_END
     */
    protected static final String PARAM_SQL_END = "~/";

    /**
     * PARAM_SQL_BEGIN
     */
    protected static final String PARAM_SQL_BEGIN = "/~";

    /**
     * REGX_SQL_PARAM
     */
    protected static final String REGX_SQL_PARAM = ".*:([\\w\\d_]+).*";

    /**
     * MACRO_PATTERN
     */
    protected static final Pattern MACRO_PATTERN = Pattern.compile("\\$\\{\\s*(\\w+)\\s*\\}");
    
    /**
     * 替换SQL语句中的宏参数 宏用${macro_name}形式表示 replaceMacro 2010-6-23
     * 增加对动态SQL构造语法支持，即包含某个参数时SQL语句包含某一段
     * 
     * @param sql
     *            原SQL，其中用${macroName}形式表示宏参数
     * @param params
     *            查询参数，含宏参数。宏参数不能为NULL
     * 
     * 如果SQL中含有宏标记，则将相应的参数作为宏进行替换。同时params中的宏参数将移除 如:
     * params={field1:"AAAA",field2:"BBBB"} sql="SELECT
     * A.${field1},A.${field1},COUNT(*) CNT FROM MY_TABLE GROUP BY
     * A.${field1},A.${field2} ORDER BY A.${field1},A.${field2} "; 执行结果为
     * sql="SELECT A.AAAA,A.BBBBCOUNT(*) CNT FROM MY_TABLE GROUP BY
     * A.AAAA,A.BBBB ORDER BY A.AAAA,A.BBBB ";
     * 
     */
    protected void replaceMacro(StringBuffer sql, Map params) {
        int k = sql.indexOf(MACRO_BEGIN);
        if (k < 0) {
            return;
        }
        int j = 0;
        Set macroNames = new HashSet();
        while (k > 0) {
            j = sql.indexOf(MACRO_END, k + 2);
            int m = sql.indexOf(MACRO_BEGIN, k + 2);
            if (j < 0) {
                throw new IllegalArgumentException("SQL宏语法解析错误，缺少【}】，位置：" + k + "\n" + sql.substring(k));
            }
            if (m > 0 && m < j) {
                throw new IllegalArgumentException("SQL宏语法错误，【${】和【}】不匹配，位置：" + k + "\n" + sql.substring(k, j + 1));
            }
            String macroName = sql.substring(k + 2, j).trim();
            macroNames.add(macroName);
            // 判断条件中是否包含了参数
            if (params.containsKey(macroName)) {
                String pValue = params.get(macroName).toString();
                sql.replace(k, j + 1, pValue);
                k = k + pValue.length();
                j = k;
            } else {// 不包含,删除语法块.改为不删除，以避免在语句中存在的${}
                // sql.delete(k, j + 1);
                // j = k;
                k = j + 2;
            }
            k = sql.indexOf(MACRO_BEGIN, k);
        }
        Iterator it = macroNames.iterator();
        while (it.hasNext()) {
            params.remove(it.next());
        }
    }

    /**
     * paramSqlParse 参数SQL语法解析 /~ and A.COLUMN_NAME = :PARAM_NAME
     * ~/类似的表达式将根据其中的参数是否在传入条件中存在进行解析 不存在则该段SQL不执行，存在则执行
     * 
     * @param sql
     *            原sql，包含语法语法块
     * @param params
     *            执行参数
     * 
     */
    protected void paramSqlParse(StringBuffer sql, Map params) {
        int k = sql.indexOf(PARAM_SQL_BEGIN);
        if (k < 0) {
            return;
        }
        int j = 0;
        while (k > 0) {
            j = sql.indexOf(PARAM_SQL_END, k + 2);
            int m = sql.indexOf(PARAM_SQL_BEGIN, k + 2);
            if (j < 0) {
                throw new IllegalArgumentException("SQL语法错误，缺少~/，位置：" + k + "\n" + sql.substring(k));
            }
            if (m > 0 && m < j) {
                throw new IllegalArgumentException("SQL语法错误，【/~】和【~/】不匹配，位置：" + k + "\n" + sql.substring(k, j + 2));
            }
            // 采用位置计算参数看速度是否有更快
            int n = sql.indexOf(":", k + 2);
            if (n > j) {
                throw new IllegalArgumentException("SQL语法错误，/~ ~/之间必须含有“:paramName”形式的参数表达式，位置" + k + "\n"
                    + sql.substring(k + 2, j));
            } else {
                // TODO 参数名后面没有空格会出错
                // int l = sql.indexOf(" ", n);
                int l = n + 2;
                for (; l < j; l++) {
                    int ch = sql.charAt(l);
                    if (!((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122) || ch == 95)) {
                        break;
                    }
                }

                if (l > j || l < 0) {
                    l = j;
                }
                String pName = sql.substring(n + 1, l);
                // 判断条件中是否包含了参数，且不为空
                if (params.containsKey(pName) && params.get(pName) != null
                    && !"".equals(params.get(pName).toString())) {
                    sql.delete(j, j + 2);
                    sql.delete(k, k + 2);
                    k = k - 2;
                    j = j - 4;
                } else {// 不包含,删除语法块及参数
                    sql.delete(k, j + 2);
                    params.remove(pName);
                    j = k;
                }
            }
            /**
             * 使用上面的算法替换，提高性能 String str = sql.substring(k + 2, j); // 解析其中的关联参数
             * if (str.matches(REGX_SQL_PARAM)) { String pName =
             * str.replaceAll(REGX_SQL_PARAM, "$1"); // 判断条件中是否包含了参数 if
             * (params.containsKey(pName)) { sql.delete(j, j + 2); sql.delete(k,
             * k + 2); k = k - 2; j = j - 4; } else {// 不包含,删除语法块 sql.delete(k,
             * j + 2); j = k; } } else { throw new
             * IllegalArgumentException("SQL语法错误，/~
             * ~/之间必须含有“:paramName”形式的参数表达式，位置"+k+"\n" + str); }
             */
            k = sql.indexOf(PARAM_SQL_BEGIN, k);
        }
    }

    /**
     * sqlParse SQL语法解析，包含宏替换和条件语法
     * 
     * @param sql
     *            原始SQL
     * @param params
     *            参数
     * @return 解析后的最终SQL
     * 
     */
    protected String sqlParse(StringBuffer sql, Map params) {
        replaceMacro(sql, params);
        paramSqlParse(sql, params);
        return sql.toString();
    }
}
